{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d006b2ea-9dfe-49c7-88a9-a5a0775185fd",
   "metadata": {},
   "source": [
    "# Additional End of week Exercise - week 2\n",
    "\n",
    "Now use everything you've learned from Week 2 to build a full prototype for the technical question/answerer you built in Week 1 Exercise.\n",
    "\n",
    "This should include a Gradio UI, streaming, use of the system prompt to add expertise, and the ability to switch between models. Bonus points if you can demonstrate use of a tool!\n",
    "\n",
    "If you feel bold, see if you can add audio input so you can talk to it, and have it respond with audio. ChatGPT or Claude can help you, or email me if you have questions.\n",
    "\n",
    "I will publish a full solution here soon - unless someone beats me to it...\n",
    "\n",
    "There are so many commercial applications for this, from a language tutor, to a company onboarding solution, to a companion AI to a course (like this one!) I can't wait to see your results."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6197b62-57fb-4569-a82d-bd4a15f4471b",
   "metadata": {},
   "source": [
    "# Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b538792e-2414-4922-bb51-95c61f6c2128",
   "metadata": {},
   "outputs": [],
   "source": [
    "# imports\n",
    "\n",
    "import os\n",
    "import requests\n",
    "import json\n",
    "from typing import List\n",
    "from dotenv import load_dotenv\n",
    "from IPython.display import Markdown, display, update_display, Audio\n",
    "from openai import OpenAI\n",
    "import anthropic\n",
    "import base64\n",
    "from io import BytesIO\n",
    "from PIL import Image\n",
    "import gradio as gr"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95afec00-4fe5-4cc9-ae0a-37c6ea4934d6",
   "metadata": {},
   "source": [
    "# OpenAI API key"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6c23ef55-daad-4ecf-893e-b83e507f2732",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load environment variables in a file called .env\n",
    "# Print the key prefixes to help with any debugging\n",
    "\n",
    "load_dotenv(override=True)\n",
    "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
    "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
    "\n",
    "\n",
    "if openai_api_key:\n",
    "    print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
    "else:\n",
    "    print(\"OpenAI API Key not set\")\n",
    "    \n",
    "if anthropic_api_key:\n",
    "    print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
    "else:\n",
    "    print(\"Anthropic API Key not set\")\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e260a7ea-fe79-4222-b6d7-7982f55a52d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "openai = OpenAI()\n",
    "\n",
    "claude = anthropic.Anthropic()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "270e937e-aef9-4032-b51e-ffa0fec184a1",
   "metadata": {},
   "source": [
    "# SYSTEM prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5639ebd6-d197-43c3-a1c4-4308b117e5ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "# set up environment\n",
    "\n",
    "def system_prompt(selected_model):\n",
    "    return f\"\"\"\n",
    "    You are a tech expert and know every coding language, and can give \n",
    "    nice, detailed and simple explanations for the given questions.\n",
    "    Introduce yourself by saying which model you are every time you answer. For example, this is {selected_model}.  \n",
    "    \n",
    "    \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1d960d3-db2e-4f7c-ab7f-d5816f3bd994",
   "metadata": {},
   "outputs": [],
   "source": [
    "def talker(message):\n",
    "    response = openai.audio.speech.create(\n",
    "        model=\"tts-1\",\n",
    "        voice=\"onyx\",\n",
    "        input=message)\n",
    "\n",
    "    audio_stream = BytesIO(response.content)\n",
    "    output_filename = \"output_audio.mp3\"\n",
    "    with open(output_filename, \"wb\") as f:\n",
    "        f.write(audio_stream.read())\n",
    "\n",
    "    display(Audio(output_filename, autoplay=True))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f39f1bc7-87c2-4b44-8ba7-c320acb33361",
   "metadata": {},
   "outputs": [],
   "source": [
    "def listener(audio_file):\n",
    "    with open(audio_file, \"rb\") as audio:\n",
    "        transcript = openai.audio.transcriptions.create(\n",
    "            model=\"whisper-1\",\n",
    "            file=audio\n",
    "        )\n",
    "    return transcript.text"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f88078a9-49ac-48b1-9f07-5b3977e88a52",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(cleared_entry, history, selected_model):\n",
    "    messages = [{\"role\": \"system\", \"content\": system_prompt(selected_model)}] + history\n",
    "\n",
    "    print(messages)\n",
    "    \n",
    "    if selected_model == \"GPT-4o-mini\":\n",
    "        stream = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages, stream=True)\n",
    "        response = \"\"\n",
    "        \n",
    "        for chunk in stream:\n",
    "            try:\n",
    "                response += chunk.choices[0].delta.content or ''\n",
    "                \n",
    "                updated_history = history + [{\"role\": \"assistant\", \"content\": response}]\n",
    "                # talker(response)\n",
    "                yield updated_history, None  \n",
    "            except Exception as e:\n",
    "                print(f\"Streaming error: {e}\")\n",
    "                yield \"Sorry, there was an error processing your request.\"\n",
    "        # talker(response)\n",
    "    elif selected_model == \"Claude-sonnet-4\":\n",
    "        claude_messages = [{\"role\": msg[\"role\"], \"content\": msg[\"content\"]} for msg in history]\n",
    "        print(claude_messages)\n",
    "        result = claude.messages.stream(\n",
    "            model=\"claude-sonnet-4-20250514\",\n",
    "            max_tokens=200,\n",
    "            temperature=0.7,\n",
    "            system=system_prompt(selected_model),  \n",
    "            messages=claude_messages,  \n",
    "        )\n",
    "        \n",
    "        response = \"\"\n",
    "        with result as stream:\n",
    "            for text in stream.text_stream:\n",
    "                response += text\n",
    "              \n",
    "                updated_history = history + [{\"role\": \"assistant\", \"content\": response}]\n",
    "                \n",
    "                yield updated_history, None  \n",
    "        # talker(response)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1863d62-a2e8-454b-9296-710c4e5f9396",
   "metadata": {},
   "outputs": [],
   "source": [
    "with gr.Blocks() as ui:\n",
    "\n",
    "    gr.Markdown(\"## AI Chat Assistant\")\n",
    "    gr.Markdown(\"**Select your preferred AI model:**\")\n",
    "    \n",
    "    model_dropdown = gr.Dropdown(\n",
    "        choices=[\"GPT-4o-mini\", \"Claude-sonnet-4\"], \n",
    "        value=\"GPT-4o-mini\",  # default selection\n",
    "        label=\"Choose Model\"\n",
    "    )\n",
    "\n",
    "    \n",
    "    with gr.Row():\n",
    "        chatbot = gr.Chatbot(height=200, type=\"messages\")\n",
    "        image_output = gr.Image(height=200)\n",
    "    with gr.Row():\n",
    "        entry = gr.Textbox(label=\"Chat with our AI Assistant:\")\n",
    "    with gr.Row():\n",
    "        # Audio input for voice messages\n",
    "        audio_input = gr.Audio(\n",
    "            sources=[\"microphone\", \"upload\"], \n",
    "            type=\"filepath\", \n",
    "            label=\"🎙️ Voice Message\"\n",
    "        )\n",
    "    with gr.Row():\n",
    "        voice_submit = gr.Button(\"Send Voice Message\", variant=\"secondary\")\n",
    "        clear = gr.Button(\"Clear\")\n",
    "\n",
    "\n",
    "    def do_entry(message, history):\n",
    "        history += [{\"role\":\"user\", \"content\":message}]\n",
    "        return \"\", history\n",
    "\n",
    "    def process_voice_input(audio_file):\n",
    "        \"\"\"Convert voice to text and put it in the text box\"\"\"\n",
    "        if audio_file is not None:\n",
    "            transcribed_text = listener(audio_file)\n",
    "            if transcribed_text and not transcribed_text.startswith(\"Error\"):\n",
    "                return transcribed_text\n",
    "        return \"\"\n",
    "\n",
    "    entry.submit(do_entry, inputs=[entry, chatbot], outputs=[entry, chatbot]).then(\n",
    "        chat, inputs=[entry,chatbot, model_dropdown], outputs=[chatbot, image_output]\n",
    "    )\n",
    "\n",
    "    voice_submit.click(\n",
    "        process_voice_input,\n",
    "        inputs=[audio_input],\n",
    "        outputs=[entry]\n",
    "    )\n",
    "    \n",
    "    clear.click(lambda: None, inputs=None, outputs=chatbot, queue=False)\n",
    "\n",
    "ui.launch(inbrowser=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "46085f59-8945-4a64-9db1-d10cd44284db",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
